package com.moxie.liveness.ui;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;


import com.moxie.liveness.MXLivenessSDK;
import com.moxie.liveness.R;
import com.moxie.liveness.network.MXLicDownloadManager;
import com.moxie.liveness.util.Constants;
import com.moxie.liveness.util.LivenessUtils;
import com.moxie.liveness.util.MXMediaPlayer;
import com.moxie.liveness.util.MXProtoBufUtil;
import com.moxie.liveness.util.MXReturnResult;
import com.moxie.liveness.util.MXSensorManager;
import com.moxie.liveness.view.CircleTimeView;
import com.moxie.liveness.view.MXAlertDialog;
import com.moxie.liveness.view.MXGifView;
import com.moxie.liveness.view.TimeViewContoller;

import java.io.File;
import java.io.InputStream;

@SuppressLint("NewApi")
public class LivenessActivity extends Activity {
    private static final String TAG = "LivenessActivity";


    public static final int RESULT_LIVENESS_CANCEL = 0;

    public static final int RESULT_CAMERA_NOT_AVAILABLE = 2;

    public static final int RESULT_RECOGNIZER_INIT_FAILED = 3;

    /**
     * 生产环境
     */
    public static final String KEY_PRODUCTION_MODE = "key_production_mode";

    private boolean isProductionMode = true;

    private static final int KEY_PERMISSION_REQUEST_LIVENESS = 103;
    private static final int KEY_PERMISSION_REQUEST_SD_CAMERA = 104;

    /**
     * 加载库文件出现错误
     */
    public static final int RESULT_CREATE_HANDLE_ERROR = 7;


    /**
     * 活体认证成功
     */
    public static final int RESULT_LIVENESS_OK = 1;

    /**
     * 无法访问摄像头，没有权限或摄像头被占用
     */
    public static final int RESULT_CAMERA_ERROR_NOPRERMISSION_OR_USED = 2;

    /**
     * 内部错误
     */
    public static final int RESULT_INTERNAL_ERROR = 5;

    /**
     * 包名绑定错误
     */
    public static final int RESULT_SDK_INIT_FAIL_APPLICATION_ID_ERROR = 8;

    /**
     * sdk初始化失败
     */
    public static final int RESULT_SDK_INIT_FAIL_OUT_OF_DATE = 3;

    /**
     * license过期
     */
    public static final int RESULT_SDK_INIT_FAIL_LICENSE_OUT_OF_DATE = 6;

    /**
     * 传入保存结果的文件路径
     */
    public static String EXTRA_RESULT_PATH = "com.moxie.liveness.resultPath";

    /**
     * 传入活体检测的动作序列
     */
    public static final String EXTRA_MOTION_SEQUENCE = "com.moxie.liveness.motionSequence";

    /**
     * 获取info信息
     */
    public static final String EXTRA_INFO = "com.moxie.liveness.info";

    /**
     * 是否打开语音提示
     */
    public static final String SOUND_NOTICE = "soundNotice";

    /**
     *  输出类型
     */
    public static final String OUTTYPE = "outType";

    /**
     * 设置复杂度
     */
    public static final String COMPLEXITY = "complexity";

    /**
     * 返回加密结果
     * */
    public static final String KEY_DETECT_RESULT = "key_detect_result";

    /**
     * 设置是否返回图片结果
     */
    public static final String KEY_DETECT_IMAGE_RESULT = "key_detect_image_result";

    /**
     * 设置是否返回video结果,只有video模式才会返回
     */
    public static final String KEY_DETECT_VIDEO_RESULT = "key_detect_video_result";

    /**
     * 设置是否返回protobuf结果
     * */
    public static final String KEY_DETECT_PROTO_BUF_RESULT = "key_detect_proto_buf_result";

    public static final String LIVENESS_FILE_NAME = "livenessResult";
    public static final String LIVENESS_VIDEO_NAME = "livenessVideoResult.mp4";

    public static String SEQUENCE_JSON = "sequence_json";

    public static String OUTPUT_TYPE = "";

    private static final int CURRENT_ANIMATION = -1;

    private MXGifView mGvView;
    private TextView mNoteTextView;
    private ViewGroup mVGBottomDots;
    private View mAnimFrame;
    private CircleTimeView mTimeView;
    private RelativeLayout mWaitDetectView;
    private LinearLayout mLlytTitle;

    private Context mContext;
    private MXAlertDialog mDialog;
    private boolean mIsStart = false, mSoundNoticeOrNot = true;
    private FaceOverlapFragment mFragment;
    private TimeViewContoller mTimeViewContoller;
    private MXMediaPlayer mMediaPlayer = new MXMediaPlayer();
    private String[] mDetectList = null;
    private MXLivenessSDK.MXLivenessMotion[] mMotionList = null;
    private MXSensorManager mSensorManger;
    private int mCurrentDetectStep = 0;

    private FaceOverlapFragment.OnLivenessCallBack mLivenessListener = new FaceOverlapFragment.OnLivenessCallBack() {
        @Override
        public void onLivenessDetect(final int value, final int status, byte[] livenessEncryptResult,
                                     byte[] videoResult, MXLivenessSDK.MXLivenessImageResult[] imageResult) {
            Log.i(TAG, "onLivenessDetect" + "***value***" + value);
            onLivenessDetectCallBack(value, status, livenessEncryptResult, videoResult, imageResult);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.moxie_activity_liveness);
        initLivenessSDK();
        mContext = this;
        mSensorManger = new MXSensorManager(this);
        Bundle bundle = getIntent().getExtras();
        mDetectList = LivenessUtils.getDetectActionOrder(bundle.getString(LivenessActivity.EXTRA_MOTION_SEQUENCE));
        mMotionList = LivenessUtils.getMctionOrder(bundle.getString(LivenessActivity.EXTRA_MOTION_SEQUENCE));
        EXTRA_RESULT_PATH = bundle.getString(LivenessActivity.EXTRA_RESULT_PATH);

        if (EXTRA_RESULT_PATH == null) {
            EXTRA_RESULT_PATH = Environment
                    .getExternalStorageDirectory().getAbsolutePath()
                    + File.separator
                    + "liveness" + File.separator;
        }
        File livenessFolder = new File(EXTRA_RESULT_PATH);
        if (!livenessFolder.exists()) {
            livenessFolder.mkdirs();
        }
        OUTPUT_TYPE = bundle.getString(Constants.OUTTYPE);
        mSoundNoticeOrNot = bundle.getBoolean(LivenessActivity.SOUND_NOTICE);

        ImageButton backBtn = (ImageButton) findViewById(R.id.linkface_return_btn);
        backBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                finish();
            }
        });
        mVGBottomDots = (ViewGroup) findViewById(R.id.viewGroup);
        if (mDetectList.length >= 1) {
            for (int i = 0; i < mDetectList.length; i++) {
                TextView tvBottomCircle = new TextView(this);
                tvBottomCircle.setBackgroundResource(R.drawable.drawable_liveness_detect_bottom_cicle_bg_selector);
                tvBottomCircle.setEnabled(true);
                LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(dp2px(8),
                        dp2px(8));
                layoutParams.leftMargin = dp2px(8);
                mVGBottomDots.addView(tvBottomCircle, layoutParams);
            }
        }
        mAnimFrame = findViewById(R.id.anim_frame);
        mAnimFrame.setVisibility(View.INVISIBLE);
        initView();
        mWaitDetectView = (RelativeLayout) findViewById(R.id.wait_time_notice);
        mWaitDetectView.setVisibility(View.VISIBLE);
        mLlytTitle = (LinearLayout) findViewById(R.id.noticeLinearLayout);
        mLlytTitle.setVisibility(View.INVISIBLE);
    }

    private void initLivenessSDK(){
        //初始化Liveness SDK的License路径
        MXLivenessSDK.getInstance(this).initLicPath(MXSensorManager.MXSDCardUtils.getSDCardBaseDir() + File.separator + Constants.LICENSE_MIDDLE_PATH + File.separator + Constants.LICENSE_NAME, Constants.LICENSE_NAME);
        int remainingDays = MXLivenessSDK.getInstance(this).getRemainingDays();
        if (!MXLivenessSDK.getInstance(this).checkLicenseValid() || remainingDays < Constants.DAYS_BEFORE_LIC_EXPIRED) {
            // 通过initWithProductionMode设置环境，true为生产环境，false为开发环境，默认是false
            // 注意上线产品设置为线上环境
            // 通过downLoadLic替换过期证书
            isProductionMode = getIntent().getBooleanExtra(KEY_PRODUCTION_MODE,false);
            MXLicDownloadManager.getInstance(this).initWithProductionMode(isProductionMode).downLoadLic();
        }
    }



    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "onPause");
        mSensorManger.unregisterListener(mSensorEventListener);
        mMediaPlayer.stop();

    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i(TAG, "onRestart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "onResume");
        if (isDialogShowing()) {
            mMediaPlayer.stop();
        }

        mSensorManger.registerListener(mSensorEventListener);
        if (mIsStart) {
            hideDialog();
            showDialog(Constants.ERROR_DETECT_FAIL);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        mIsStart = true;
        Log.i(TAG, "onStop");
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if (mFragment != null) {
            mFragment.registerLivenessDetectCallback(null);
            mFragment = null;
        }
        if (mTimeViewContoller != null) {
            mTimeViewContoller.setCallBack(null);
            mTimeViewContoller = null;
        }
    }

    private void initView() {
        mGvView = (MXGifView) findViewById(R.id.id_gv_play_action);
        mNoteTextView = (TextView) findViewById(R.id.noteText);
        mTimeView = (CircleTimeView) findViewById(R.id.time_view);
        mTimeViewContoller = new TimeViewContoller(mTimeView);
        mFragment = (FaceOverlapFragment) getFragmentManager()
                .findFragmentById(R.id.overlapFragment);
        mFragment.registerLivenessDetectCallback(mLivenessListener);

        ImageView ivMask = (ImageView) findViewById(R.id.image_mask);
        ivMask.setImageBitmap(readBitMap(this, R.drawable.moxie_mask_background));
    }

    /**
     * 以最省内存的方式读取本地资源的图片
     *
     * @param context
     * @param resId
     * @return
     */
    public static Bitmap readBitMap(Context context, int resId) {
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inPreferredConfig = Bitmap.Config.RGB_565;
        opt.inPurgeable = true;
        opt.inInputShareable = true;
        //获取资源图片
        InputStream is = context.getResources().openRawResource(resId);
        return BitmapFactory.decodeStream(is, null, opt);
    }

    private String getStringWithID(int id) {
        return getResources().getString(id);
    }

    private void startAnimation(int animation) {
        if (animation != CURRENT_ANIMATION) {
            mGvView.setMovieResource(animation);
            if (isDialogShowing()) {
                return;
            }
        }
        mTimeViewContoller.start();
        mTimeViewContoller.setCallBack(new TimeViewContoller.CallBack() {
            @Override
            public void onTimeEnd() {
                if (null == mFragment) {
                    return;
                }
                mFragment.onTimeEnd();
            }
        });
    }

    private void setLivenessState(boolean pause) {
        if (null == mFragment) {
            return;
        }
        if (pause) {
            mFragment.stopLiveness();
        } else {
            mFragment.startLiveness();
        }
    }

    private void onLivenessDetectCallBack(final int value, final int status, final byte[] livenessEncryptResult, final byte[] videoResult, final MXLivenessSDK.MXLivenessImageResult[] imageResult) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mCurrentDetectStep = status + 1;
                if (value == MXLivenessSDK.MXLivenessMotion.BLINK.getValue()) {
                    updateUi(R.string.note_blink, R.raw.raw_liveness_detect_blink, status + 1);
                } else if (value == MXLivenessSDK.MXLivenessMotion.MOUTH.getValue()) {
                    updateUi(R.string.note_mouth, R.raw.raw_liveness_detect_mouth, status + 1);
                } else if (value == MXLivenessSDK.MXLivenessMotion.NOD.getValue()) {
                    updateUi(R.string.note_nod, R.raw.raw_liveness_detect_nod, status + 1);
                } else if (value == MXLivenessSDK.MXLivenessMotion.YAW.getValue()) {
                    updateUi(R.string.note_yaw, R.raw.raw_liveness_detect_yaw, status + 1);
                } else if (value == Constants.LIVENESS_SUCCESS) {
                    updateTheLastStepUI(mVGBottomDots);
                    saveFinalEncrytFile(livenessEncryptResult, videoResult, imageResult);
                } else if (value == Constants.LIVENESS_TRACKING_MISSED) {
                    showDialog(Constants.ERROR_ACTION_GET_FAIL);
                    LivenessUtils.saveFile(livenessEncryptResult, EXTRA_RESULT_PATH, LIVENESS_FILE_NAME);
                } else if (value == Constants.LIVENESS_TIME_OUT) {
                    showDialog(Constants.ERROR_ACTION_TIME_OUT);
                    LivenessUtils.saveFile(livenessEncryptResult, EXTRA_RESULT_PATH, LIVENESS_FILE_NAME);
                } else if (value == Constants.DETECT_BEGIN_WAIT) {
                    showDetectWaitUI();
                } else if (value == Constants.DETECT_END_WAIT) {
                    removeDetectWaitUI();
                }
            }
        });
    }

    private void showDetectWaitUI() {
        mWaitDetectView.setVisibility(View.VISIBLE);
        mIsStart = true;
        if (mTimeViewContoller != null) {
            mTimeViewContoller.setCallBack(null);
        }
        mLlytTitle.setVisibility(View.INVISIBLE);
    }

    private void removeDetectWaitUI() {
        mWaitDetectView.setVisibility(View.GONE);
        setLivenessState(false);
        mLlytTitle.setVisibility(View.VISIBLE);
        mAnimFrame.setVisibility(View.VISIBLE);
        onLivenessDetectCallBack(mMotionList[0].getValue(), 0, null, null, null);
    }

    private void updateTheLastStepUI(ViewGroup viewGroup) {
        mMediaPlayer.release();
    }

    private void updateUi(int stringId, int animationId, int number) {
        mNoteTextView.setText(getStringWithID(stringId));
        if (animationId != 0) {
            startAnimation(animationId);
        }
        if (number - 2 >= 0) {
            View childAt = mVGBottomDots.getChildAt(number - 2);
            childAt.setEnabled(false);
        }
        playSoundNotice(number);
    }

    private void playSoundNotice(int step) {

        if (step > 0) {
            if (mDetectList[step - 1]
                    .equalsIgnoreCase(getString(R.string.blink))) {
                if (mSoundNoticeOrNot) {
                    mMediaPlayer.setMediaSource(mContext, "linkface_notice_blink.mp3", true);
                }
            } else if (mDetectList[step - 1]
                    .equalsIgnoreCase(getString(R.string.nod))) {
                if (mSoundNoticeOrNot) {
                    mMediaPlayer.setMediaSource(mContext, "linkface_notice_nod.mp3", true);
                }
            } else if (mDetectList[step - 1]
                    .equalsIgnoreCase(getString(R.string.mouth))) {
                if (mSoundNoticeOrNot) {
                    mMediaPlayer.setMediaSource(mContext, "linkface_notice_mouth.mp3", true);
                }
            } else if (mDetectList[step - 1]
                    .equalsIgnoreCase(getString(R.string.yaw))) {
                if (mSoundNoticeOrNot) {
                    mMediaPlayer.setMediaSource(mContext, "linkface_notice_yaw.mp3", true);
                }
            }
        }
    }

    public void saveFinalEncrytFile(byte[] livenessEncryptResult, byte[] videoResult, MXLivenessSDK.MXLivenessImageResult[] imageResult) {
        Intent intent = new Intent();

        MXReturnResult returnResult = new MXReturnResult();
        boolean isReturnImage = getIntent().getBooleanExtra(KEY_DETECT_IMAGE_RESULT, true);
        boolean isReturnProtoBuf = getIntent().getBooleanExtra(KEY_DETECT_PROTO_BUF_RESULT, true);

        if (isReturnImage) {
            returnResult.setImageResults(imageResult);
        }

        if(isReturnProtoBuf && livenessEncryptResult != null && livenessEncryptResult.length > 0) {
            MXProtoBufUtil.setProtoBuf(livenessEncryptResult);
        }

        if (videoResult != null) {
            String fileAbsolutePath = LivenessUtils.saveFile(videoResult, EXTRA_RESULT_PATH, LIVENESS_VIDEO_NAME);
            boolean isReturnVideo = getIntent().getBooleanExtra(KEY_DETECT_VIDEO_RESULT, true);
            if (isReturnVideo) {
                returnResult.setVideoResultPath(fileAbsolutePath);
            }
        }
        intent.putExtra(KEY_DETECT_RESULT, returnResult);

        if (livenessEncryptResult != null) {
//            LivenessUtils.saveFile(livenessEncryptResult, EXTRA_RESULT_PATH, LIVENESS_FILE_NAME);
            setResult(RESULT_LIVENESS_OK, intent);
            finish();
        }
    }

    private void showDialog(String message) {
        if (isDialogShowing()) {
            return;
        }
        if (mDetectList.length >= 1) {
            for (int i = 0; i < mDetectList.length; i++) {
                View childAt = mVGBottomDots.getChildAt(i);
                if (childAt != null) {
                    childAt.setEnabled(true);
                }
            }
        }

        hideTimeContoller();
        hideIndicateView();
        mDialog = new MXAlertDialog(mContext).builder().setCancelable(false).
                setTitle(message).setNegativeButton(getStringWithID(R.string.cancel), new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                onErrorHappen(RESULT_CANCELED);
            }
        }).setPositiveButton(getStringWithID(R.string.restart_preview), new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                showIndicateView();
                if (mFragment != null) {
                    mFragment.registerLivenessDetectCallback(mLivenessListener);
                }
                restartAnimationAndLiveness();
            }
        });
        if (((Activity) mContext).isFinishing()) {
            return;
        }
        mDialog.show();
        mMediaPlayer.release();
    }

    private void hideDialog() {
        if (mDialog != null && mDialog.isShowing()) {
            mDialog.dismiss();
        }
    }

    private void hideIndicateView() {
        if (mGvView != null) {
            mGvView.setVisibility(View.GONE);
        }
        if (mVGBottomDots != null) {
            mVGBottomDots.setVisibility(View.GONE);
        }
        if (mNoteTextView != null) {
            mNoteTextView.setVisibility(View.GONE);
        }
    }

    private void showIndicateView() {
        if (mGvView != null) {
            mGvView.setVisibility(View.VISIBLE);
        }
        if (mVGBottomDots != null) {
            mVGBottomDots.setVisibility(View.VISIBLE);
        }
        if (mNoteTextView != null) {
            mNoteTextView.setVisibility(View.VISIBLE);
        }
    }

    private void hideTimeContoller() {
        if (mTimeViewContoller != null) {
            mTimeViewContoller.hide();
        }
    }

    private void restartAnimationAndLiveness() {
        setLivenessState(false);
        LivenessUtils.deleteFiles(EXTRA_RESULT_PATH);
        if (mDetectList.length >= 1) {
            View childAt = mVGBottomDots.getChildAt(0);
            childAt.setEnabled(false);
        }
        startAnimation(CURRENT_ANIMATION);
        mMediaPlayer.release();
        playSoundNotice(mCurrentDetectStep);
    }

    private boolean isDialogShowing() {
        return mDialog != null && mDialog.isShowing();
    }

    public void onErrorHappen(int resultCode) {
        setResult(resultCode);
        finish();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString(Constants.RESULT, "活体检测被取消!");
        intent.putExtras(bundle);
        setResult(RESULT_CANCELED, intent);
        finish();
    }

    SensorEventListener mSensorEventListener = new SensorEventListener() {

        @Override
        public void onAccuracyChanged(Sensor arg0, int arg1) {
        }

        @Override
        public void onSensorChanged(SensorEvent event) {
            mFragment.addSequentialInfo(event.sensor.getType(), event.values);
        }
    };

    private int dp2px(float dpValue) {
        int densityDpi = this.getResources().getDisplayMetrics().densityDpi;
        return (int) (dpValue * (densityDpi / 160));
    }
}
